home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2001 May / may_2001.iso / intercd / root / Multimedia / ^DivX_Article / virtualdub / VirtualDub-source-1_4d / Job.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-03-20  |  40.4 KB  |  1,740 lines

  1. //    VirtualDub - Video processing and capture application
  2. //    Copyright (C) 1998-2001 Avery Lee
  3. //
  4. //    This program is free software; you can redistribute it and/or modify
  5. //    it under the terms of the GNU General Public License as published by
  6. //    the Free Software Foundation; either version 2 of the License, or
  7. //    (at your option) any later version.
  8. //
  9. //    This program is distributed in the hope that it will be useful,
  10. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. //    GNU General Public License for more details.
  13. //
  14. //    You should have received a copy of the GNU General Public License
  15. //    along with this program; if not, write to the Free Software
  16. //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. #include "VirtualDub.h"
  19.  
  20. #include <stdio.h>
  21. #include <crtdbg.h>
  22.  
  23. #include <windows.h>
  24. #include <commctrl.h>
  25. #include <commdlg.h>
  26. #include <shellapi.h>
  27. #include <shlobj.h>
  28.  
  29. #include "resource.h"
  30.  
  31. #include "List.h"
  32. #include "Error.h"
  33. #include "InputFile.h"
  34.  
  35. #include "gui.h"
  36. #include "job.h"
  37. #include "command.h"
  38. #include "dub.h"
  39. #include "script.h"
  40.  
  41. ///////////////////////////////////////////////////////////////////////////
  42.  
  43. extern HWND g_hWnd;
  44. extern HINSTANCE g_hInst;
  45. extern FilterFunctions g_filterFuncs;
  46. extern char g_szInputAVIFile[];
  47. extern char g_szInputWAVFile[];
  48. extern char g_szInputAVIFileTitle[];
  49. extern InputFileOptions *g_pInputOpts;
  50.  
  51. HWND g_hwndJobs;
  52.  
  53. bool g_fJobMode;
  54. bool g_fJobAborted;
  55.  
  56. ///////////////////////////////////////////////////////////////////////////
  57.  
  58. static BOOL CALLBACK JobCtlDlgProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam);
  59.  
  60. ///////////////////////////////////////////////////////////////////////////
  61.  
  62. static char *strCify(const char *s) {
  63.     static char buf[2048];
  64.     char c,*t = buf;
  65.  
  66.     while(c=*s++) {
  67.         if (!isprint((unsigned char)c))
  68.             t += sprintf(t, "\\x%02x", (int)c & 0xff);
  69.         else {
  70.             if (c=='"' || c=='\\')
  71.                 *t++ = '\\';
  72.             *t++ = c;
  73.         }
  74.     }
  75.     *t=0;
  76.  
  77.     return buf;
  78. }
  79.  
  80. ///////////////////////////////////////////////////////////////////////////
  81.  
  82. class JobScriptOutputBlock : public ListNode {
  83. public:
  84.     long size;
  85.     long ptr;
  86.     char data[];
  87. };
  88.  
  89. class JobScriptOutput {
  90. private:
  91.     List listScript;
  92.     JobScriptOutputBlock *jsob;
  93.     long total;
  94. public:
  95.     JobScriptOutput();
  96.     ~JobScriptOutput();
  97.  
  98.     void clear();
  99.     void write(const char *s, long l);
  100.     void adds(const char *s);
  101.     void addf(const char *fmt, ...);
  102.     char *getscript();
  103. };
  104.  
  105. ///////
  106.  
  107. JobScriptOutput::JobScriptOutput() {
  108.     clear();
  109. }
  110.  
  111. JobScriptOutput::~JobScriptOutput() {
  112.     clear();
  113. }
  114.  
  115. void JobScriptOutput::clear() {
  116.     JobScriptOutputBlock *jsob;
  117.  
  118.     while(jsob = (JobScriptOutputBlock *)listScript.RemoveHead())
  119.         freemem(jsob);
  120.  
  121.     total = 0;
  122.     this->jsob = NULL;
  123. }
  124.  
  125. void JobScriptOutput::write(const char *s, long l) {
  126.     long to_copy;
  127.  
  128.     total += l;
  129.  
  130.     while(l) {
  131.         if (jsob && jsob->ptr<jsob->size) {
  132.             to_copy = jsob->size-jsob->ptr;
  133.             if (to_copy > l) to_copy = l;
  134.  
  135.             memcpy(jsob->data + jsob->ptr, s, to_copy);
  136.             jsob->ptr += to_copy;
  137.             l -= to_copy;
  138.         } else {
  139.             jsob = (JobScriptOutputBlock *)allocmem(16384);
  140.  
  141.             if (!jsob) throw MyMemoryError();
  142.  
  143.             jsob->size = 16384 - sizeof(JobScriptOutputBlock);
  144.             jsob->ptr = 0;
  145.             listScript.AddTail(jsob);
  146.         }
  147.     }
  148. }
  149.  
  150. void JobScriptOutput::adds(const char *s) {
  151.     long l = strlen(s);
  152.  
  153.     write(s, l);
  154.     write("\n",1);
  155. //    _RPT1(0,">%s\n",s);
  156. }
  157.  
  158. void JobScriptOutput::addf(const char *fmt, ...) {
  159.     char buf[2048];
  160.     va_list val;
  161.     long l;
  162.  
  163.     va_start(val, fmt);
  164.     _vsnprintf(buf, sizeof buf, fmt, val);
  165.     va_end(val);
  166.     buf[sizeof buf-1]=0;
  167.  
  168.     l = strlen(buf);
  169.     adds(buf);
  170. }
  171.  
  172. char *JobScriptOutput::getscript() {
  173.     char *mem = (char *)allocmem(total+1), *t=mem;
  174.     JobScriptOutputBlock *jsobptr;
  175.     if (!mem) throw MyMemoryError();
  176.  
  177.     jsobptr = (JobScriptOutputBlock *)listScript.head.prev;
  178.  
  179.     while(jsobptr->prev) {
  180.         memcpy(t, jsobptr->data, jsobptr->ptr);
  181.         t += jsobptr->ptr;
  182.  
  183.         jsobptr = (JobScriptOutputBlock *)jsobptr->prev;
  184.     }
  185.     *t = 0;
  186.  
  187.     return mem;
  188. }
  189.  
  190. ///////////////////////////////////////////////////////////////////////////
  191.  
  192. class VDJob : public ListNode {
  193. private:
  194.     static List job_list;
  195.     static long job_count;
  196.     static bool fRunInProgress;
  197.     static bool fRunAllStop;
  198.     static bool fModified;
  199.  
  200. public:
  201.     static long job_number;
  202.  
  203.     enum {
  204.         WAITING        = 0,
  205.         INPROGRESS    = 1,
  206.         DONE        = 2,
  207.         POSTPONED    = 3,
  208.         ABORTED        = 4,
  209.         ERR            = 5,
  210.     };
  211.  
  212.     char szName[64];
  213.     char szInputFile[MAX_PATH];
  214.     char szOutputFile[MAX_PATH];
  215.     char *szInputFileTitle;
  216.     char *szOutputFileTitle;
  217.     char szError[256];
  218.     int iState;
  219.     SYSTEMTIME stStart, stEnd;
  220.     char *script;
  221.  
  222.     /////
  223.  
  224.     VDJob();
  225.     ~VDJob();
  226.  
  227.     void Add(bool force_no_update = false);
  228.     void Delete(bool force_no_update = false);
  229.     void Refresh();
  230.  
  231.  
  232.     void Run();
  233.  
  234.  
  235.     static VDJob *ListGet(int index);
  236.     static int ListFind(VDJob *vdj_find);
  237.     static long ListSize();
  238.     static void ListClear(bool force_no_update = false);
  239.     static void ListLoad(char *lpszName = NULL);
  240.  
  241.     static bool IsModified() {
  242.         return fModified;
  243.     }
  244.  
  245.     static void SetModified();
  246.  
  247.     static void Flush(char *lpfn =NULL);
  248.     static void RunAll();
  249.     static void RunAllStop();
  250.  
  251.     static bool IsRunInProgress() {
  252.         return fRunInProgress;
  253.     }
  254. };
  255.  
  256. List VDJob::job_list;
  257. long VDJob::job_count;
  258. long VDJob::job_number=1;
  259. bool VDJob::fModified = false;
  260. bool VDJob::fRunInProgress = false;
  261. bool VDJob::fRunAllStop;
  262.  
  263. VDJob::VDJob() {
  264.     szName[0]=0;
  265.     szInputFile[0]=0;
  266.     szOutputFile[0]=0;
  267.     iState = VDJob::WAITING;
  268.     stStart.wYear = stEnd.wYear = 0;
  269.     script = NULL;
  270. }
  271.  
  272. VDJob::~VDJob() {
  273.     delete script;
  274. }
  275.  
  276. void VDJob::Add(bool force_no_update) {
  277.     char c, *s;
  278.  
  279.     s = szInputFileTitle = szInputFile;
  280.     while(c=*s++) if (c=='\\' || c==':') szInputFileTitle=s;
  281.     
  282.     s = szOutputFileTitle = szOutputFile;
  283.     while(c=*s++) if (c=='\\' || c==':') szOutputFileTitle=s;
  284.  
  285.     job_list.AddHead(this);
  286.     ++job_count;
  287.  
  288.     if (g_hwndJobs) {
  289.         LVITEM li;
  290.  
  291.         li.mask        = LVIF_TEXT;
  292.         li.iSubItem    = 0;
  293.         li.iItem    = job_count-1;
  294.         li.pszText    = LPSTR_TEXTCALLBACK;
  295.  
  296.         ListView_InsertItem(GetDlgItem(g_hwndJobs, IDC_JOBS), &li);
  297.     }
  298.  
  299.     if (!force_no_update) SetModified();
  300. }
  301.  
  302. void VDJob::Delete(bool force_no_update) {
  303.     int index = ListFind(this);
  304.  
  305.     if (index>=0 && g_hwndJobs)
  306.         ListView_DeleteItem(GetDlgItem(g_hwndJobs, IDC_JOBS), index);
  307.  
  308.     ListNode::Remove();
  309.     --job_count;
  310.     
  311.     if (!force_no_update) SetModified();
  312. }
  313.  
  314. void VDJob::Refresh() {
  315.     int index = ListFind(this);
  316. //    bool fSelected;
  317.  
  318.     if (index>=0 && g_hwndJobs) {
  319.         HWND hwndItem = GetDlgItem(g_hwndJobs, IDC_JOBS);
  320.  
  321.         ListView_Update(hwndItem, index);
  322.     }
  323. }
  324.  
  325. void VDJob::Run() {
  326.     iState = INPROGRESS;
  327.     GetLocalTime(&stStart);
  328.     memset(&stEnd, 0, sizeof(SYSTEMTIME));
  329.     Refresh();
  330.     Flush();
  331.  
  332.     strcpy(g_szInputAVIFile, szInputFile);
  333.     strcpy(g_szInputAVIFileTitle, szInputFileTitle);
  334.  
  335.     EnableWindow(GetDlgItem(g_hwndJobs, IDC_PROGRESS), TRUE);
  336.     EnableWindow(GetDlgItem(g_hwndJobs, IDC_PERCENT), TRUE);
  337.  
  338.     try {
  339.         g_fJobMode = true;
  340.         g_fJobAborted = false;
  341.         _CrtCheckMemory();
  342.         RunScriptMemory(script);
  343.         _CrtCheckMemory();
  344.         g_fJobMode = false;
  345.     } catch(MyError err) {
  346.         iState = ERR;
  347.         strcpy(szError, err.gets());
  348.     }
  349.  
  350.     EnableWindow(GetDlgItem(g_hwndJobs, IDC_PROGRESS), FALSE);
  351.     EnableWindow(GetDlgItem(g_hwndJobs, IDC_PERCENT), FALSE);
  352.  
  353.     if (iState == INPROGRESS) {
  354.         if (g_fJobAborted)
  355.             iState = ABORTED;
  356.         else
  357.             iState = DONE;
  358.     }
  359.     GetLocalTime(&stEnd);
  360.     Refresh();
  361.     Flush();
  362. }
  363.  
  364. ////////
  365.  
  366. VDJob *VDJob::ListGet(int index) {
  367.     VDJob *vdj = (VDJob *)job_list.tail.next, *vdj_next;
  368.  
  369.     while((vdj_next = (VDJob *)vdj->next) && index--)
  370.         vdj = vdj_next;
  371.  
  372.     if (!vdj_next) return NULL;
  373.  
  374.     return vdj;
  375. }
  376.  
  377. int VDJob::ListFind(VDJob *vdj_find) {
  378.     VDJob *vdj = (VDJob *)job_list.tail.next, *vdj_next;
  379.     int index=0;
  380.  
  381.     while((vdj_next = (VDJob *)vdj->next) && vdj != vdj_find) {
  382.         vdj = vdj_next;
  383.         ++index;
  384.     }
  385.  
  386.     if (vdj == vdj_find) return index;
  387.  
  388.     return -1;
  389. }
  390.  
  391. long VDJob::ListSize() {
  392.     return job_count;
  393. }
  394.  
  395. // VDJob::ListClear()
  396. //
  397. // Clears all jobs from the list.
  398.  
  399. void VDJob::ListClear(bool force_no_update) {
  400.     VDJob *vdj;
  401.  
  402.     while((vdj = (VDJob *)job_list.tail.next)->next) {
  403.         vdj->Delete(true);
  404.         delete vdj;
  405.     }
  406.  
  407.     if (!force_no_update) SetModified();
  408. }
  409.  
  410. // VDJob::ListLoad()
  411. //
  412. // Loads the list from a file.
  413.  
  414. static char *findcmdline(char *s) {
  415.     while(isspace(*s)) ++s;
  416.  
  417.     if (s[0] != '/' || s[1] != '/')
  418.         return NULL;
  419.  
  420.     s+=2;
  421.  
  422.     while(isspace(*s)) ++s;
  423.     if (*s++ != '$') return NULL;
  424.  
  425.     return s;
  426. }
  427.  
  428. static void strgetarg(char *buf, long bufsiz, const char *s) {
  429.     const char *t = s;
  430.     long l;
  431.  
  432.     if (*t == '"') {
  433.         s = ++t;
  434.         while(*s && *s!='"') ++s;
  435.     } else
  436.         while(*s && !isspace(*s)) ++s;
  437.  
  438.     l = s-t;
  439.     if (l > bufsiz-1)
  440.         l = bufsiz-1;
  441.  
  442.     memcpy(buf, t, l);
  443.     buf[l]=0;
  444. }
  445.  
  446. void VDJob::ListLoad(char *lpszName) {
  447.     FILE *f = NULL;
  448.     char szName[MAX_PATH], szVDPath[MAX_PATH], *lpFilePart;
  449.     char linebuf[2048];
  450.     VDJob *job = NULL;
  451.  
  452.     // Try to create VirtualDub.jobs in the same directory as VirtualDub.
  453.  
  454.     if (!lpszName) {
  455.         lpszName = szName;
  456.  
  457.         if (!GetModuleFileName(NULL, szVDPath, sizeof szVDPath))
  458.             return;
  459.  
  460.         if (!GetFullPathName(szVDPath, sizeof szName, szName, &lpFilePart))
  461.             return;
  462.  
  463.         strcpy(lpFilePart, "VirtualDub.jobs");
  464.     }
  465.     try {
  466.         BOOL script_capture = false;
  467.         JobScriptOutput jso;
  468.  
  469.         f = fopen(lpszName, "r");
  470.         if (!f) return;
  471.  
  472.         ListClear(true);
  473.  
  474.         while(fgets(linebuf, sizeof linebuf, f)) {
  475.             char *s;
  476.  
  477.             // kill the ending newline
  478.  
  479.             if (s = strchr(linebuf,'\n'))
  480.                 *s = 0;
  481.  
  482.             // scan for a command
  483.  
  484.             if (s = findcmdline(linebuf)) {
  485.                 char *t = s;
  486.  
  487.                 while(isalpha(*t) || *t=='_') ++t;
  488.  
  489.                 if (*t) *t++=0;
  490.                 while(isspace(*t)) ++t;
  491.  
  492.                 if (!stricmp(s, "job")) {
  493.                     if (!(job = new VDJob)) throw "out of memory";
  494.                     job->szError[0]=0;
  495.  
  496.                     strgetarg(job->szName, sizeof job->szName, t);
  497.  
  498.                 } else if (!stricmp(s, "input")) {
  499.  
  500.                     strgetarg(job->szInputFile, sizeof job->szInputFile, t);
  501.  
  502.                 } else if (!stricmp(s, "output")) {
  503.  
  504.                     strgetarg(job->szOutputFile, sizeof job->szOutputFile, t);
  505.  
  506.                 } else if (!stricmp(s, "error")) {
  507.  
  508.                     strgetarg(job->szError, sizeof job->szError, t);
  509.  
  510.                 } else if (!stricmp(s, "state")) {
  511.  
  512.                     job->iState = atoi(t);
  513.  
  514.                     // Make sure "In Progress" states change to Aborted
  515.  
  516.                     if (job->iState == INPROGRESS)
  517.                         job->iState = ABORTED;
  518.  
  519.                 } else if (!stricmp(s, "start_time")) {
  520.                     FILETIME ft;
  521.  
  522.                     if (2 != sscanf(t, "%08lx %08lx", &ft.dwHighDateTime, &ft.dwLowDateTime))
  523.                         throw "invalid start time";
  524.  
  525.                     if (!ft.dwHighDateTime && !ft.dwLowDateTime)
  526.                         memset(&job->stStart, 0, sizeof(SYSTEMTIME));
  527.                     else
  528.                         FileTimeToSystemTime(&ft, &job->stStart);
  529.  
  530.                 } else if (!stricmp(s, "end_time")) {
  531.                     FILETIME ft;
  532.  
  533.                     if (2 != sscanf(t, "%08lx %08lx", &ft.dwHighDateTime, &ft.dwLowDateTime))
  534.                         throw "invalid start time";
  535.  
  536.                     if (!ft.dwHighDateTime && !ft.dwLowDateTime)
  537.                         memset(&job->stEnd, 0, sizeof(SYSTEMTIME));
  538.                     else
  539.                         FileTimeToSystemTime(&ft, &job->stEnd);
  540.  
  541.                 } else if (!stricmp(s, "script")) {
  542.  
  543.                     script_capture = true;
  544.  
  545.                 } else if (!stricmp(s, "endjob")) {
  546.                     if (script_capture) {
  547.                         job->script = jso.getscript();
  548.                         jso.clear();
  549.                         script_capture = false;
  550.                     }
  551.  
  552.                     job->Add(true);
  553.                     job = NULL;;
  554.                 }
  555.             } else if (script_capture) {
  556.                 // kill starting spaces
  557.  
  558.                 s = linebuf;
  559.  
  560.                 while(isspace(*s)) ++s;
  561.  
  562.                 // don't add blank lines
  563.  
  564.                 if (*s)
  565.                     jso.adds(s);
  566.             }
  567.         }
  568.  
  569.     } catch(int e) {
  570.         _RPT0(0,"I/O error on job load\n");
  571.         if (lpszName != szName) {
  572.             if (f) fclose(f);
  573.             throw MyError("Failure loading job list: %s.", strerror(e));
  574.         }
  575.     } catch(char *s) {
  576.         _RPT0(0,s);
  577.         if (lpszName != szName) {
  578.             if (f) fclose(f);
  579.             throw MyError(s);
  580.         }
  581.     }
  582.  
  583.     if (f) fclose(f);
  584.     delete job;
  585. }
  586.  
  587. void VDJob::SetModified() {
  588.     fModified = true;
  589.  
  590.     if (!g_hwndJobs)
  591.         Flush();
  592. }
  593.  
  594. // VDJob::Flush()
  595. //
  596. // Flushes the job list out to disk.
  597. //
  598. // We store the job list in a file called VirtualDub.jobs.  It's actually a
  599. // human-readable, human-editable Sylia script with extra comments to tell
  600. // VirtualDub about each of the scripts.
  601.  
  602. void VDJob::Flush(char *lpszFileName) {
  603.     FILE *f = NULL;
  604.     char szName[MAX_PATH], szVDPath[MAX_PATH], *lpFilePart;
  605.  
  606.     _RPT0(0,"VDJob::Flush()\n");
  607.  
  608.     // Try to create VirtualDub.jobs in the same directory as VirtualDub.
  609.  
  610.     if (!lpszFileName) {
  611.         if (!GetModuleFileName(NULL, szVDPath, sizeof szVDPath))
  612.             return;
  613.  
  614.         if (!GetFullPathName(szVDPath, sizeof szName, szName, &lpFilePart))
  615.             return;
  616.  
  617.         strcpy(lpFilePart, "VirtualDub.jobs");
  618.  
  619.         lpszFileName = szName;
  620.     }
  621.  
  622.     try {
  623.         VDJob *vdj, *vdj_next;
  624.  
  625.         f = fopen(lpszFileName, "w");
  626.         if (!f) throw errno;
  627.  
  628.         if (fprintf(f,
  629.                 "// VirtualDub job list (Sylia script format)\n"
  630.                 "// This is a program generated file -- edit at your own risk.\n"
  631.                 "//\n"
  632.                 "// $numjobs %d\n"
  633.                 "//\n\n"
  634.                 ,VDJob::ListSize()
  635.                 )<0)
  636.             throw errno;
  637.  
  638.         vdj = (VDJob *)job_list.tail.next;
  639.  
  640.         while(vdj_next = (VDJob *)vdj->next) {
  641.             FILETIME ft;
  642.             char *s, *t, c;
  643.  
  644.             if (fprintf(f,"// $job \"%s\""            "\n", vdj->szName)<0) throw errno;
  645.             if (fprintf(f,"// $input \"%s\""        "\n", vdj->szInputFile)<0) throw errno;
  646.             if (fprintf(f,"// $output \"%s\""        "\n", vdj->szOutputFile)<0) throw errno;
  647.             if (fprintf(f,"// $state %d"            "\n", vdj->iState)<0) throw errno;
  648.  
  649.             if (vdj->stStart.wYear) {
  650.                 SystemTimeToFileTime(&vdj->stStart, &ft);
  651.                 if (fprintf(f,"// $start_time %08lx %08lx"    "\n", ft.dwHighDateTime, ft.dwLowDateTime)<0) throw errno;
  652.             } else
  653.                 if (fprintf(f,"// $start_time 0 0\n")<0) throw errno;
  654.  
  655.             if (vdj->stEnd.wYear) {
  656.                 SystemTimeToFileTime(&vdj->stEnd, &ft);
  657.                 if (fprintf(f,"// $end_time %08lx %08lx"    "\n", ft.dwHighDateTime, ft.dwLowDateTime)<0) throw errno;
  658.             } else
  659.                 if (fprintf(f,"// $end_time 0 0\n")<0) throw errno;
  660.  
  661.             if (vdj->iState == ERR)
  662.                 if (fprintf(f,"// $error \"%s\"\n", vdj->szError)<0) throw errno;
  663.  
  664.             if (fprintf(f,"// $script\n\n")<0) throw errno;
  665.  
  666.             // Dump script
  667.  
  668.             s = vdj->script;
  669.  
  670.             while(*s) {
  671.                 t=s;
  672.  
  673.                 while((c=*t) && c!='\r' && c!='\n')
  674.                     ++t;
  675.  
  676.                 if (t>s)
  677.                     if (1!=fwrite(s, t-s, 1, f)) throw errno;
  678.                 if (EOF==putc('\n', f)) throw errno;
  679.  
  680.                 // handle CR, CR/LF, LF, and NUL terminators
  681.  
  682.                 if (c == '\r') ++t;
  683.                 if (c == '\n') ++t;
  684.  
  685.                 s=t;
  686.             }
  687.  
  688.             // Next...
  689.  
  690.             if (fputs(
  691.                     "\n"
  692.                     "// $endjob\n"
  693.                     "//\n"
  694.                     "//--------------------------------------------------\n"
  695.                 ,f)==EOF) throw errno;
  696.  
  697.             vdj = (VDJob *)vdj->next;
  698.         }
  699.  
  700.         if (fprintf(f,"// $done\n")<0) throw errno;
  701.  
  702.         if (fflush(f)) throw errno;
  703.  
  704.         if (lpszFileName == szName) fModified = false;
  705.  
  706.     } catch(int) {
  707.         _RPT0(0,"I/O error on job flush\n");
  708.         if (lpszFileName) {
  709.             if (f) fclose(f);
  710.             throw MyError("Job list flush failed: %s.", strerror(errno));
  711.         }
  712.     }
  713.  
  714.     if (f) fclose(f);
  715. }
  716.  
  717. void VDJob::RunAll() {
  718.     VDJob *vdj = (VDJob *)job_list.tail.next;
  719.  
  720.     fRunInProgress    = true;
  721.     fRunAllStop        = false;
  722.  
  723.     if (g_hwndJobs) {
  724.         SetDlgItemText(g_hwndJobs, IDC_START, "Stop");
  725.         EnableWindow(GetDlgItem(g_hwndJobs, IDC_ABORT), TRUE);
  726.     }
  727.  
  728.     ShowWindow(g_hWnd, SW_MINIMIZE);
  729.  
  730.     while((VDJob *)vdj->next && !fRunAllStop) {
  731.         if (vdj->iState == WAITING)
  732.             vdj->Run();
  733.  
  734.         vdj = (VDJob *)vdj->next;
  735.     }
  736.  
  737. //    ShowWindow(g_hWnd, SW_RESTORE);
  738.  
  739.     fRunInProgress = false;
  740.  
  741.     if (g_hwndJobs) {
  742.         EnableWindow(GetDlgItem(g_hwndJobs, IDC_START), TRUE);
  743.         SetDlgItemText(g_hwndJobs, IDC_START, "Start");
  744.         EnableWindow(GetDlgItem(g_hwndJobs, IDC_ABORT), FALSE);
  745.     }
  746. }
  747.  
  748. void VDJob::RunAllStop() {
  749.     fRunAllStop = true;
  750. }
  751.  
  752. ///////////////////////////////////////////////////////////////////////////
  753.  
  754. static BOOL CALLBACK JobErrorDlgProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) {
  755.  
  756.     switch(uiMsg) {
  757.     case WM_INITDIALOG:
  758.         {
  759.             VDJob *vdj = (VDJob *)lParam;
  760.             char buf[128];
  761.  
  762.             wsprintf(buf, "VirtualDub - Job \"%s\"", vdj->szName);
  763.             SetWindowText(hdlg, buf);
  764.  
  765.             SetDlgItemText(hdlg, IDC_ERROR, vdj->szError);
  766.         }
  767.         return TRUE;
  768.  
  769.     case WM_COMMAND:
  770.         if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
  771.             EndDialog(hdlg, 0);
  772.             return TRUE;
  773.         }
  774.         break;
  775.     }
  776.  
  777.     return FALSE;
  778. }
  779.  
  780. static void Job_GetDispInfo(NMLVDISPINFO *nldi) {
  781.     VDJob *vdj = VDJob::ListGet(nldi->item.iItem);
  782.     SYSTEMTIME *st = &vdj->stEnd;
  783.     SYSTEMTIME ct;
  784.     static const char *dow[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
  785.  
  786.     nldi->item.mask            = LVIF_TEXT;
  787.     nldi->item.pszText[0]    = 0;
  788.  
  789.     switch(nldi->item.iSubItem) {
  790.     case 0:
  791.         nldi->item.pszText = vdj->szName;
  792.         break;
  793.     case 1:        // file in
  794.         nldi->item.pszText = vdj->szInputFileTitle;
  795.         break;
  796.     case 2:        // file out
  797.         nldi->item.pszText = vdj->szOutputFileTitle;
  798.         break;
  799.     case 3:        // time in
  800.         st = &vdj->stStart;
  801.     case 4:        // time out
  802.         GetLocalTime(&ct);
  803.         if (!st->wYear)
  804.             nldi->item.pszText = "-";
  805.         else if (ct.wYear != st->wYear
  806.             || ct.wMonth != st->wMonth
  807.             || ct.wDay != st->wDay) {
  808.  
  809.             _snprintf(nldi->item.pszText, nldi->item.cchTextMax, "%s %d %d:%02d%c"
  810.                         ,dow[st->wDayOfWeek]
  811.                         ,st->wDay
  812.                         ,st->wHour==12||!st->wHour ? 12 : st->wHour%12
  813.                         ,st->wMinute
  814.                         ,st->wHour>=12 ? 'p' : 'a');
  815.         } else {
  816.             _snprintf(nldi->item.pszText, nldi->item.cchTextMax, "%d:%02d%c"
  817.                         ,st->wHour==12||!st->wHour ? 12 : st->wHour%12
  818.                         ,st->wMinute
  819.                         ,st->wHour>=12 ? 'p' : 'a');
  820.         }
  821.         break;
  822.     case 5:        // status
  823.         switch(vdj->iState) {
  824.         case VDJob::WAITING:    nldi->item.pszText = "Waiting"        ; break;
  825.         case VDJob::INPROGRESS:    nldi->item.pszText = "In progress"    ; break;
  826.         case VDJob::DONE:        nldi->item.pszText = "Done"            ; break;
  827.         case VDJob::POSTPONED:    nldi->item.pszText = "Postponed"    ; break;
  828.         case VDJob::ABORTED:    nldi->item.pszText = "Aborted"        ; break;
  829.         case VDJob::ERR:        nldi->item.pszText = "Error"        ; break;
  830.         }
  831.         break;
  832.     }
  833. }
  834.  
  835.  
  836. static void JobProcessDirectory(HWND hDlg) {
  837.     char szSourceDir[MAX_PATH];
  838.     char szDestDir[MAX_PATH];
  839.     char *lpszFileName;
  840.     BROWSEINFO bi;
  841.     LPITEMIDLIST pidlBrowse;
  842.     LPMALLOC pMalloc;
  843.     bool fAbort = false;
  844.  
  845.     if (SUCCEEDED(SHGetMalloc(&pMalloc))) {
  846.         if (lpszFileName = (char *)pMalloc->Alloc(MAX_PATH)) {
  847.             bi.hwndOwner        = hDlg;
  848.             bi.pidlRoot            = NULL;
  849.             bi.pszDisplayName    = lpszFileName;
  850.             bi.lpszTitle        = "Select source directory";
  851.             bi.ulFlags            = BIF_RETURNONLYFSDIRS;
  852.             bi.lpfn                = NULL;
  853.  
  854.             if (pidlBrowse = SHBrowseForFolder(&bi)) {
  855.                 if (SHGetPathFromIDList(pidlBrowse, lpszFileName))
  856.                     strcpy(szSourceDir, lpszFileName);
  857.                 else
  858.                     fAbort = true;
  859.  
  860.                 pMalloc->Free(pidlBrowse);
  861.             }
  862.  
  863.             bi.lpszTitle        = "Select destination directory";
  864.  
  865.             if (!fAbort && (pidlBrowse = SHBrowseForFolder(&bi))) {
  866.                 if (SHGetPathFromIDList(pidlBrowse, lpszFileName))
  867.                     strcpy(szDestDir, lpszFileName);
  868.  
  869.                 pMalloc->Free(pidlBrowse);
  870.             }
  871.             pMalloc->Free(lpszFileName);
  872.         }
  873.     }
  874.  
  875.     if (fAbort) return;
  876.  
  877.     JobAddBatchDirectory(szSourceDir, szDestDir);
  878.  
  879. }
  880.  
  881. void Job_MenuHit(HWND hdlg, WPARAM wParam) {
  882.     static char *fileFilters=
  883.         "VirtualDub job list (*.jobs)\0"        "*.jobs\0"
  884.         "Sylia script for VirtualDub (*.syl)\0"    "*.syl\0"
  885.         "All files (*.*)\0"                        "*.*\0";
  886.     VDJob *vdj, *vdj_next;
  887.  
  888.     try {
  889.         switch(LOWORD(wParam)) {
  890.  
  891.             case ID_FILE_LOADJOBLIST:
  892.                 {
  893.                     OPENFILENAME ofn;
  894.                     char szFile[MAX_PATH];
  895.  
  896.                     ///////////////
  897.  
  898.                     szFile[0] = 0;
  899.  
  900.                     ofn.lStructSize            = sizeof(OPENFILENAME);
  901.                     ofn.hwndOwner            = hdlg;
  902.                     ofn.lpstrFilter            = fileFilters;
  903.                     ofn.lpstrCustomFilter    = NULL;
  904.                     ofn.nFilterIndex        = 1;
  905.                     ofn.lpstrFile            = szFile;
  906.                     ofn.nMaxFile            = sizeof szFile;
  907.                     ofn.lpstrFileTitle        = NULL;
  908.                     ofn.nMaxFileTitle        = 0;
  909.                     ofn.lpstrInitialDir        = NULL;
  910.                     ofn.lpstrTitle            = "Load job list";
  911.                     ofn.Flags                = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_ENABLESIZING;
  912.                     ofn.lpstrDefExt            = NULL;
  913.  
  914.                     if (GetOpenFileName(&ofn))
  915.                         VDJob::ListLoad(szFile);
  916.                 }
  917.                 break;
  918.  
  919.             case ID_FILE_SAVEJOBLIST:
  920.                 {
  921.                     OPENFILENAME ofn;
  922.                     char szFile[MAX_PATH];
  923.  
  924.                     ///////////////
  925.  
  926.                     szFile[0] = 0;
  927.  
  928.                     ofn.lStructSize            = sizeof(OPENFILENAME);
  929.                     ofn.hwndOwner            = hdlg;
  930.                     ofn.lpstrFilter            = fileFilters;
  931.                     ofn.lpstrCustomFilter    = NULL;
  932.                     ofn.nFilterIndex        = 1;
  933.                     ofn.lpstrFile            = szFile;
  934.                     ofn.nMaxFile            = sizeof szFile;
  935.                     ofn.lpstrFileTitle        = NULL;
  936.                     ofn.nMaxFileTitle        = 0;
  937.                     ofn.lpstrInitialDir        = NULL;
  938.                     ofn.lpstrTitle            = "Save job list";
  939.                     ofn.Flags                = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLESIZING;
  940.                     ofn.lpstrDefExt            = NULL;
  941.  
  942.                     if (GetSaveFileName(&ofn))
  943.                         vdj->Flush(szFile);
  944.                 }
  945.                 break;
  946.  
  947.             case ID_EDIT_CLEARLIST:
  948.                 if (IDOK != MessageBox(hdlg, "Really clear job list?", "VirtualDub job system", MB_OKCANCEL | MB_ICONEXCLAMATION))
  949.                     break;
  950.  
  951.                 VDJob::ListClear(false);
  952.                 break;
  953.  
  954.             case ID_EDIT_DELETEDONEJOBS:
  955.  
  956.                 if (!(vdj = VDJob::ListGet(0)))
  957.                     break;
  958.  
  959.                 while(vdj_next = (VDJob *)vdj->next) {
  960.                     if (vdj->iState == VDJob::DONE) {
  961.                         vdj->Delete();
  962.                         delete vdj;
  963.                     }
  964.  
  965.                     vdj = vdj_next;
  966.                 }
  967.  
  968.                 break;
  969.  
  970.             case ID_EDIT_FAILEDTOWAITING
  971.                 :
  972.  
  973.                 if (!(vdj = VDJob::ListGet(0)))
  974.                     break;
  975.  
  976.                 while(vdj_next = (VDJob *)vdj->next) {
  977.                     if (vdj->iState == VDJob::ABORTED || vdj->iState == VDJob::ERR) {
  978.                         vdj->iState = VDJob::WAITING;
  979.                         vdj->Refresh();
  980.                     }
  981.  
  982.                     vdj = vdj_next;
  983.                 }
  984.                 break;
  985.  
  986.             case ID_EDIT_WAITINGTOPOSTPONED:
  987.  
  988.                 if (!(vdj = VDJob::ListGet(0)))
  989.                     break;
  990.  
  991.                 while(vdj_next = (VDJob *)vdj->next) {
  992.                     if (vdj->iState == VDJob::WAITING) {
  993.                         vdj->iState = VDJob::POSTPONED;
  994.                         vdj->Refresh();
  995.                     }
  996.  
  997.                     vdj = vdj_next;
  998.                 }
  999.                 break;
  1000.  
  1001.             case ID_EDIT_POSTPONEDTOWAITING:
  1002.  
  1003.                 if (!(vdj = VDJob::ListGet(0)))
  1004.                     break;
  1005.  
  1006.                 while(vdj_next = (VDJob *)vdj->next) {
  1007.                     if (vdj->iState == VDJob::POSTPONED) {
  1008.                         vdj->iState = VDJob::WAITING;
  1009.                         vdj->Refresh();
  1010.                     }
  1011.  
  1012.                     vdj = vdj_next;
  1013.                 }
  1014.                 break;
  1015.  
  1016.             case ID_EDIT_DONETOWAITING:
  1017.  
  1018.                 if (!(vdj = VDJob::ListGet(0)))
  1019.                     break;
  1020.  
  1021.                 while(vdj_next = (VDJob *)vdj->next) {
  1022.                     if (vdj->iState == VDJob::DONE) {
  1023.                         vdj->iState = VDJob::WAITING;
  1024.                         vdj->Refresh();
  1025.                     }
  1026.  
  1027.                     vdj = vdj_next;
  1028.                 }
  1029.                 break;
  1030.  
  1031.             case ID_EDIT_PROCESSDIRECTORY:
  1032.                 JobProcessDirectory(hdlg);
  1033.                 break;
  1034.         }
  1035.     } catch(MyError e) {
  1036.         e.post(hdlg, "Job system error");
  1037.     }
  1038. }
  1039.  
  1040. static struct ReposItem jobCtlPosData[]={
  1041.     { IDOK            , REPOS_MOVERIGHT },
  1042.     { IDC_MOVE_UP    , REPOS_MOVERIGHT },
  1043.     { IDC_MOVE_DOWN    , REPOS_MOVERIGHT },
  1044.     { IDC_POSTPONE    , REPOS_MOVERIGHT },
  1045.     { IDC_DELETE    , REPOS_MOVERIGHT },
  1046.     { IDC_START        , REPOS_MOVERIGHT },
  1047.     { IDC_ABORT        , REPOS_MOVERIGHT },
  1048.     { IDC_JOBS        , REPOS_SIZERIGHT | REPOS_SIZEDOWN },
  1049.     { IDC_CURRENTJOB, REPOS_MOVEDOWN },
  1050.     { IDC_PROGRESS    , REPOS_MOVEDOWN | REPOS_SIZERIGHT },
  1051.     { IDC_PERCENT    , REPOS_MOVEDOWN | REPOS_MOVERIGHT },
  1052.     { 0 }
  1053. };
  1054.  
  1055. POINT jobCtlPos[11];
  1056.  
  1057. static BOOL CALLBACK JobCtlDlgProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) {
  1058.     static char *szColumnNames[]={ "Name","Source","Dest","Start","End","Status" };
  1059.     static int iColumnWidths[]={ 100,75,75,50,50,100 };
  1060.  
  1061.     static UINT uiUpdateTimer;
  1062.     static int iUpdateVal;
  1063.     static bool fUpdateDisable;
  1064.     static RECT rInitial;
  1065.  
  1066.     VDJob *vdj, *vdj_p, *vdj_p2, *vdj_n, *vdj_n2;
  1067.     int index;
  1068.     HWND hwndItem;
  1069.  
  1070.     switch(uiMsg) {
  1071.     case WM_INITDIALOG:
  1072.         {
  1073.             HWND hwndItem;
  1074.             LV_COLUMN lvc;
  1075.             int i;
  1076.  
  1077.             GetWindowRect(hdlg, &rInitial);
  1078.  
  1079.             hwndItem = GetDlgItem(hdlg, IDC_JOBS);
  1080.             fUpdateDisable = false;
  1081.  
  1082.             ListView_SetExtendedListViewStyleEx(hwndItem, LVS_EX_FULLROWSELECT , LVS_EX_FULLROWSELECT);
  1083.  
  1084.             for (i=0; i<6; i++) {
  1085.                 lvc.mask = LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH;
  1086.                 lvc.fmt = LVCFMT_LEFT;
  1087.                 lvc.cx = iColumnWidths[i];
  1088.                 lvc.pszText = szColumnNames[i];
  1089.  
  1090.                 ListView_InsertColumn(hwndItem, i, &lvc);
  1091.             }
  1092.  
  1093.             for(i=0; i<VDJob::ListSize(); i++) {
  1094.                 LVITEM li;
  1095.  
  1096.                 li.mask        = LVIF_TEXT;
  1097.                 li.iSubItem    = 0;
  1098.                 li.iItem    = i;
  1099.                 li.pszText    = LPSTR_TEXTCALLBACK;
  1100.  
  1101.                 ListView_InsertItem(hwndItem, &li);
  1102.             }
  1103.  
  1104.             if (g_dubber) {
  1105.                 EnableWindow(GetDlgItem(hdlg, IDC_START), FALSE);
  1106.                 EnableWindow(GetDlgItem(hdlg, IDC_ABORT), FALSE);
  1107.             } else if (VDJob::IsRunInProgress()) {
  1108.                 SetDlgItemText(hdlg, IDC_START, "Stop");
  1109.             } else {
  1110.                 EnableWindow(GetDlgItem(hdlg, IDC_ABORT), FALSE);
  1111.             }
  1112.  
  1113.             SendDlgItemMessage(hdlg, IDC_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, 16384));
  1114.  
  1115.             if (g_dubber || VDJob::IsRunInProgress()) {
  1116.                 EnableWindow(GetDlgItem(hdlg, IDC_PROGRESS), FALSE);
  1117.                 EnableWindow(GetDlgItem(hdlg, IDC_PERCENT), FALSE);
  1118.             } else {
  1119.                 EnableWindow(GetDlgItem(hdlg, IDC_PROGRESS), TRUE);
  1120.                 EnableWindow(GetDlgItem(hdlg, IDC_PERCENT), TRUE);
  1121.             }
  1122.  
  1123.             guiReposInit(hdlg, jobCtlPosData, jobCtlPos);
  1124.  
  1125.             uiUpdateTimer = SetTimer(hdlg, 1, 5000, NULL);
  1126.  
  1127.             iUpdateVal = 0;
  1128.         }
  1129.         return FALSE;
  1130.  
  1131.     case WM_TIMER:
  1132.  
  1133.         // Wait for two intervals to flush.  That way, if the job list
  1134.         // is set modified right before a timer message, we don't flush
  1135.         // immediately.
  1136.  
  1137.         if (VDJob::IsModified()) {
  1138.             if (iUpdateVal) {
  1139.                 VDJob::Flush();
  1140.                 iUpdateVal = 0;
  1141.             } else
  1142.                 ++iUpdateVal;
  1143.         } else
  1144.             iUpdateVal = 0;
  1145.  
  1146.         return TRUE;
  1147.  
  1148.     case WM_NOTIFY:
  1149.         {
  1150.             NMHDR *nm = (NMHDR *)lParam;
  1151.  
  1152.             if (nm->idFrom == IDC_JOBS) {
  1153.                 NMLVDISPINFO *nldi = (NMLVDISPINFO *)nm;
  1154.                 NMLISTVIEW *nmlv;
  1155.                 VDJob *vdj;
  1156.                 bool fSelected;
  1157.  
  1158.                 switch(nm->code) {
  1159.                 case LVN_GETDISPINFO:
  1160.                     Job_GetDispInfo(nldi);
  1161.                     return TRUE;
  1162.                 case LVN_ENDLABELEDIT:
  1163.                     SetWindowLong(hdlg, DWL_MSGRESULT, TRUE);
  1164.                     vdj = VDJob::ListGet(nldi->item.iItem);
  1165.  
  1166.                     if (vdj && nldi->item.pszText) {
  1167.                         strncpy(vdj->szName, nldi->item.pszText, sizeof vdj->szName);
  1168.                         vdj->szName[sizeof vdj->szName-1] = 0;
  1169.                     }
  1170.                     return TRUE;
  1171.                 case LVN_ITEMCHANGED:
  1172.  
  1173.                     if (fUpdateDisable) return TRUE;
  1174.  
  1175.                     nmlv = (NMLISTVIEW *)lParam;
  1176.                     vdj = VDJob::ListGet(nmlv->iItem);
  1177.  
  1178.                     _RPT3(0,"Item %d, subitem %d: new state is %s\n",nmlv->iItem,nmlv->iSubItem,nmlv->uNewState & LVIS_SELECTED ? "selected" : "unselected");
  1179.                     fSelected = !!(nmlv->uNewState & LVIS_SELECTED);
  1180.  
  1181.                     EnableWindow(GetDlgItem(hdlg, IDC_MOVE_UP), fSelected && nmlv->iItem>0);
  1182.                     EnableWindow(GetDlgItem(hdlg, IDC_MOVE_DOWN), fSelected && nmlv->iItem<VDJob::ListSize()-1);
  1183.                     EnableWindow(GetDlgItem(hdlg, IDC_DELETE), fSelected && (!vdj || vdj->iState != VDJob::INPROGRESS));
  1184.                     EnableWindow(GetDlgItem(hdlg, IDC_POSTPONE), fSelected && (!vdj || vdj->iState != VDJob::INPROGRESS));
  1185.                     return TRUE;
  1186.  
  1187.                 case LVN_KEYDOWN:
  1188.                     switch(((LPNMLVKEYDOWN)lParam)->wVKey) {
  1189.                     case VK_DELETE:
  1190.                         SendMessage(hdlg, WM_COMMAND, IDC_DELETE, (LPARAM)GetDlgItem(hdlg, IDC_DELETE));
  1191.                     }
  1192.                     return TRUE;
  1193.  
  1194.                 case NM_DBLCLK:
  1195.  
  1196.                     //    Previous state        Next state        Action
  1197.                     //    --------------        ----------        ------
  1198.                     //    Error                Waiting            Show error message
  1199.                     //    Done                Waiting
  1200.                     //    Postponed            Waiting
  1201.                     //    Aborted                Waiting
  1202.                     //    All others            Postponed
  1203.  
  1204.                     index = ListView_GetNextItem(GetDlgItem(hdlg, IDC_JOBS), -1, LVNI_ALL | LVNI_SELECTED);
  1205.                     if (index>=0) {
  1206.                         vdj = VDJob::ListGet(index);
  1207.  
  1208.                         switch(vdj->iState) {
  1209.                         case VDJob::ERR:
  1210.                             DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_JOBERROR), hdlg, JobErrorDlgProc, (LPARAM)vdj);
  1211.                         case VDJob::DONE:
  1212.                         case VDJob::ABORTED:
  1213.                             vdj->iState = VDJob::WAITING;
  1214.                             vdj->Refresh();
  1215.                             VDJob::SetModified();
  1216.                         case VDJob::INPROGRESS:
  1217.                             break;
  1218.                         default:
  1219.                             SendMessage(hdlg, WM_COMMAND, MAKELONG(IDC_POSTPONE, BN_CLICKED), (LPARAM)GetDlgItem(hdlg, IDC_POSTPONE));
  1220.                         }
  1221.                     }
  1222.  
  1223.                     return TRUE;
  1224.                 }
  1225.             }
  1226.         }
  1227.         break;
  1228.  
  1229.     case WM_DESTROY:
  1230.         _RPT0(0,"Destroy caught -- flushing job list.\n");
  1231.         g_hwndJobs = NULL;
  1232.         VDJob::Flush();
  1233.         return TRUE;
  1234.  
  1235.     case WM_CLOSE:
  1236.         DestroyWindow(hdlg);
  1237.         return TRUE;
  1238.  
  1239.     case WM_COMMAND:
  1240.         if (!lParam) {
  1241.             // menu hit
  1242.  
  1243.             Job_MenuHit(hdlg, wParam);
  1244.             return TRUE;
  1245.  
  1246.         } else if (HIWORD(wParam) == BN_CLICKED) {
  1247.             hwndItem = GetDlgItem(hdlg, IDC_JOBS);
  1248.  
  1249.             index = ListView_GetNextItem(hwndItem, -1, LVNI_ALL | LVNI_SELECTED);
  1250.             if (index>=0)
  1251.                 vdj = VDJob::ListGet(index);
  1252.             else
  1253.                 vdj = NULL;
  1254.  
  1255.             switch(LOWORD(wParam)) {
  1256.  
  1257.             case IDOK:
  1258.                 DestroyWindow(hdlg);
  1259.                 return TRUE;
  1260.  
  1261.             case IDC_DELETE:
  1262.                 if (vdj) {
  1263.                     // Do not delete jobs that are in progress!
  1264.  
  1265.                     if (vdj->iState != VDJob::INPROGRESS) {
  1266.                         fUpdateDisable = true;
  1267.                         vdj->Delete();
  1268.                         delete vdj;
  1269.                         VDJob::SetModified();
  1270.                         fUpdateDisable = false;
  1271.                         if (VDJob::ListSize() > 0)
  1272.                             ListView_SetItemState(hwndItem, index==VDJob::ListSize() ? index-1 : index, LVIS_SELECTED, LVIS_SELECTED);
  1273.                     }
  1274.                 }
  1275.  
  1276.                 return TRUE;
  1277.  
  1278.             case IDC_POSTPONE:
  1279.                 if (vdj) {
  1280.                     // Do not postpone jobs in progress
  1281.  
  1282.                     if (vdj->iState != VDJob::INPROGRESS) {
  1283.                         if (vdj->iState == VDJob::POSTPONED)
  1284.                             vdj->iState = VDJob::WAITING;
  1285.                         else
  1286.                             vdj->iState = VDJob::POSTPONED;
  1287.  
  1288.                         vdj->Refresh();
  1289.  
  1290.                         VDJob::SetModified();
  1291.                     }
  1292.                 }
  1293.  
  1294.                 return TRUE;
  1295.  
  1296.             case IDC_MOVE_UP:
  1297.                 if (!vdj || index <= 0)
  1298.                     return TRUE;
  1299.  
  1300.                 vdj_n    = (VDJob *)vdj->next;
  1301.                 vdj_p    = (VDJob *)vdj->prev;
  1302.                 vdj_p2    = (VDJob *)vdj_p->prev;
  1303.  
  1304.                 vdj_p2->next = vdj;        vdj->prev = vdj_p2;
  1305.                 vdj->next = vdj_p;        vdj_p->prev = vdj;
  1306.                 vdj_p->next = vdj_n;    vdj_n->prev = vdj_p;
  1307.  
  1308.                 ListView_SetItemState(hwndItem, index  , 0, LVIS_SELECTED | LVIS_FOCUSED);
  1309.                 ListView_SetItemState(hwndItem, index-1, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
  1310.                 ListView_RedrawItems(hwndItem, index-1, index);
  1311.  
  1312.                 VDJob::SetModified();
  1313.  
  1314.                 return TRUE;
  1315.  
  1316.             case IDC_MOVE_DOWN:
  1317.                 if (!vdj || index >= VDJob::ListSize()-1)
  1318.                     return TRUE;
  1319.  
  1320.                 vdj_p    = (VDJob *)vdj->prev;
  1321.                 vdj_n    = (VDJob *)vdj->next;
  1322.                 vdj_n2    = (VDJob *)vdj_n->next;
  1323.  
  1324.                 vdj_p->next = vdj_n;    vdj_n->prev = vdj_p;
  1325.                 vdj->prev = vdj_n;        vdj_n->next = vdj;
  1326.                 vdj_n2->prev = vdj;        vdj->next = vdj_n2;
  1327.                 
  1328.                 ListView_SetItemState(hwndItem, index  , 0, LVIS_SELECTED | LVIS_FOCUSED);
  1329.                 ListView_SetItemState(hwndItem, index+1, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
  1330.                 ListView_RedrawItems(hwndItem, index, index+1);
  1331.  
  1332.                 VDJob::SetModified();
  1333.  
  1334.                 return TRUE;
  1335.  
  1336.             case IDC_START:
  1337.                 if (VDJob::IsRunInProgress()) {
  1338.                     VDJob::RunAllStop();
  1339.                     EnableWindow((HWND)lParam, FALSE);
  1340.                 } else
  1341.                     VDJob::RunAll();
  1342.                 return TRUE;
  1343.  
  1344.             case IDC_ABORT:
  1345.                 if (VDJob::IsRunInProgress()) {
  1346.                     VDJob::RunAllStop();
  1347.                     EnableWindow(GetDlgItem(hdlg, IDC_START), FALSE);
  1348.                     EnableWindow((HWND)lParam, FALSE);
  1349.                     if (g_dubber) g_dubber->Abort();
  1350.                 }
  1351.  
  1352.                 return TRUE;
  1353.  
  1354.             }
  1355.         }
  1356.         break;
  1357.  
  1358.     case WM_GETMINMAXINFO:
  1359.         {
  1360.             LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam;
  1361.  
  1362.             lpmmi->ptMinTrackSize.x = rInitial.right - rInitial.left;
  1363.             lpmmi->ptMinTrackSize.y = rInitial.bottom - rInitial.top;
  1364.         }
  1365.         return TRUE;
  1366.  
  1367.     case WM_SIZE:
  1368.         guiReposResize(hdlg, jobCtlPosData, jobCtlPos);
  1369.         return TRUE;
  1370.  
  1371.     }
  1372.     return FALSE;
  1373. }
  1374.  
  1375. ///////////////////////////////////////////////////////////////////////////
  1376.  
  1377. void JobCreateScript(JobScriptOutput& output, const DubOptions *opt) {
  1378.     char *mem= NULL;
  1379.     char buf[2048];
  1380.     long l;
  1381.  
  1382.     switch(audioInputMode) {
  1383.     case AUDIOIN_WAVE:
  1384.         output.addf("VirtualDub.audio.SetSource(\"%s\");", strCify(g_szInputWAVFile));
  1385.         break;
  1386.  
  1387.     default:
  1388.         output.addf("VirtualDub.audio.SetSource(%d);", audioInputMode);
  1389.         break;
  1390.     
  1391.     }
  1392.  
  1393.     output.addf("VirtualDub.audio.SetMode(%d);", opt->audio.mode);
  1394.  
  1395.     output.addf("VirtualDub.audio.SetInterleave(%d,%d,%d,%d,%d);",
  1396.             opt->audio.enabled,
  1397.             opt->audio.preload,
  1398.             opt->audio.interval,
  1399.             opt->audio.is_ms,
  1400.             opt->audio.offset);
  1401.  
  1402.     output.addf("VirtualDub.audio.SetClipMode(%d,%d);",
  1403.             opt->audio.fStartAudio,
  1404.             opt->audio.fEndAudio);
  1405.  
  1406.     output.addf("VirtualDub.audio.SetConversion(%d,%d,%d,%d,%d);",
  1407.             opt->audio.new_rate,
  1408.             opt->audio.newPrecision,
  1409.             opt->audio.newChannels,
  1410.             opt->audio.integral_rate,
  1411.             opt->audio.fHighQuality);
  1412.  
  1413.     if (opt->audio.volume)
  1414.         output.addf("VirtualDub.audio.SetVolume(%d);", opt->audio.volume);
  1415.     else
  1416.         output.addf("VirtualDub.audio.SetVolume();");
  1417.  
  1418.     if (g_ACompressionFormat) {
  1419.         if (g_ACompressionFormat->cbSize) {
  1420.             mem = (char *)allocmem(((g_ACompressionFormat->cbSize+2)/3)*4 + 1);
  1421.             if (!mem) throw MyMemoryError();
  1422.  
  1423.             membase64(mem, (char *)(g_ACompressionFormat+1), g_ACompressionFormat->cbSize);
  1424.             output.addf("VirtualDub.audio.SetCompression(%d,%d,%d,%d,%d,%d,%d,\"%s\");"
  1425.                         ,g_ACompressionFormat->wFormatTag
  1426.                         ,g_ACompressionFormat->nSamplesPerSec
  1427.                         ,g_ACompressionFormat->nChannels
  1428.                         ,g_ACompressionFormat->wBitsPerSample
  1429.                         ,g_ACompressionFormat->nAvgBytesPerSec
  1430.                         ,g_ACompressionFormat->nBlockAlign
  1431.                         ,g_ACompressionFormat->cbSize
  1432.                         ,mem
  1433.                         );
  1434.  
  1435.             freemem(mem);
  1436.         } else
  1437.             output.addf("VirtualDub.audio.SetCompression(%d,%d,%d,%d,%d,%d);"
  1438.                         ,g_ACompressionFormat->wFormatTag
  1439.                         ,g_ACompressionFormat->nSamplesPerSec
  1440.                         ,g_ACompressionFormat->nChannels
  1441.                         ,g_ACompressionFormat->wBitsPerSample
  1442.                         ,g_ACompressionFormat->nAvgBytesPerSec
  1443.                         ,g_ACompressionFormat->nBlockAlign
  1444.                         );
  1445.     } else
  1446.         output.addf("VirtualDub.audio.SetCompression();");
  1447.  
  1448.     output.addf("VirtualDub.video.SetDepth(%d,%d);",
  1449.             16+8*opt->video.inputDepth,
  1450.             16+8*opt->video.outputDepth);
  1451.  
  1452.     output.addf("VirtualDub.video.SetMode(%d);",
  1453.             opt->video.mode);
  1454.  
  1455.     output.addf("VirtualDub.video.SetFrameRate(%d,%d);",
  1456.             opt->video.frameRateNewMicroSecs,
  1457.             opt->video.frameRateDecimation);
  1458.  
  1459.     output.addf("VirtualDub.video.SetIVTC(%d,%d,%d,%d);",
  1460.             opt->video.fInvTelecine,
  1461.             opt->video.fIVTCMode,
  1462.             opt->video.nIVTCOffset,
  1463.             opt->video.fIVTCPolarity);
  1464.  
  1465.     output.addf("VirtualDub.video.SetRange(%d,%d);",
  1466.             opt->video.lStartOffsetMS,
  1467.             opt->video.lEndOffsetMS);
  1468.  
  1469.     if (g_Vcompression.dwFlags & ICMF_COMPVARS_VALID) {
  1470.         output.addf("VirtualDub.video.SetCompression(0x%08lx,%d,%d,%d);",
  1471.                 g_Vcompression.fccHandler,
  1472.                 g_Vcompression.lKey,
  1473.                 g_Vcompression.lQ,
  1474.                 g_Vcompression.lDataRate);
  1475.  
  1476.         l = ICGetStateSize(g_Vcompression.hic);
  1477.  
  1478.         if (l>0) {
  1479.             mem = (char *)allocmem(l + ((l+2)/3)*4 + 1);
  1480.             if (!mem) throw MyMemoryError();
  1481.  
  1482.             if (ICGetState(g_Vcompression.hic, mem, l)<0) {
  1483.                 freemem(mem);
  1484. //                throw MyError("Bad state data returned from compressor");
  1485.  
  1486.                 // Fine then, be that way.  Stupid Pinnacle DV200 driver.
  1487.             }
  1488.  
  1489.             if (mem) {
  1490.                 membase64(mem+l, mem, l);
  1491.                 output.addf("VirtualDub.video.SetCompData(%d,\"%s\");", l, mem+l);
  1492.                 freemem(mem);
  1493.             }
  1494.         }
  1495.  
  1496.     } else
  1497.         output.addf("VirtualDub.video.SetCompression();");
  1498.  
  1499.     output.addf("VirtualDub.video.filters.Clear();");
  1500.  
  1501.     // Add filters
  1502.  
  1503.     FilterInstance *fa = (FilterInstance *)g_listFA.tail.next, *fa_next;
  1504.     int iFilter = 0;
  1505.  
  1506.     while(fa_next = (FilterInstance *)fa->next) {
  1507.         output.addf("VirtualDub.video.filters.Add(\"%s\");", strCify(fa->filter->name));
  1508.  
  1509.         if (fa->x1 || fa->y1 || fa->x2 || fa->y2)
  1510.             output.addf("VirtualDub.video.filters.instance[%d].SetClipping(%d,%d,%d,%d);"
  1511.                         ,iFilter
  1512.                         ,fa->x1
  1513.                         ,fa->y1
  1514.                         ,fa->x2
  1515.                         ,fa->y2
  1516.                         );
  1517.  
  1518.         if (fa->filter->fssProc && fa->filter->fssProc(fa, &g_filterFuncs, buf, sizeof buf))
  1519.             output.addf("VirtualDub.video.filters.instance[%d].%s;", iFilter, buf);
  1520.  
  1521.         ++iFilter;
  1522.         fa = fa_next;
  1523.     }
  1524.  
  1525.     // Add subset information
  1526.  
  1527.     if (inputSubset) {
  1528.         FrameSubsetNode *pfsn;
  1529.  
  1530.         output.addf("VirtualDub.subset.Clear();");
  1531.  
  1532.         if (pfsn = inputSubset->getFirstFrame())
  1533.             do {
  1534.                 output.addf("VirtualDub.subset.AddFrame(%ld,%ld);", pfsn->start, pfsn->len);
  1535.             } while(pfsn = inputSubset->getNextFrame(pfsn));
  1536.     } else
  1537.         output.addf("VirtualDub.subset.Delete();");
  1538.  
  1539.  
  1540. }
  1541.  
  1542. void JobAddConfiguration(const DubOptions *opt, const char *szFileInput, int iFileMode, const char *szFileOutput, bool fCompatibility, List2<InputFilenameNode> *pListAppended, long lSpillThreshold, long lSpillFrameThreshold) {
  1543.     VDJob *vdj = new VDJob;
  1544.     JobScriptOutput output;
  1545.  
  1546.     try {
  1547.  
  1548.         do {
  1549.             if (g_pInputOpts) {
  1550.                 int l;
  1551.                 char buf[256];
  1552.  
  1553.                 l = g_pInputOpts->write(buf, (sizeof buf)/7*3);
  1554.  
  1555.                 if (l) {
  1556.                     membase64(buf+l, (char *)buf, l);
  1557.                     output.addf("VirtualDub.Open(\"%s\",%d,0,\"%s\");",strCify(szFileInput), iFileMode, buf+l);
  1558.                     break;
  1559.                 }
  1560.             }
  1561.  
  1562.             output.addf("VirtualDub.Open(\"%s\",%d,0);",strCify(szFileInput), iFileMode);
  1563.  
  1564.         } while(false);
  1565.  
  1566.         if (pListAppended) {
  1567.             InputFilenameNode *ifn = pListAppended->AtHead(), *ifn_next;
  1568.  
  1569.             if (ifn = ifn->NextFromHead())
  1570.                 while(ifn_next = ifn->NextFromHead()) {
  1571.                     output.addf("VirtualDub.Append(\"%s\");", strCify(ifn->name));
  1572.                     ifn = ifn_next;
  1573.                 }
  1574.         }
  1575.  
  1576.         JobCreateScript(output, opt);
  1577.  
  1578.         // Add actual run option
  1579.  
  1580.         if (lSpillThreshold)
  1581.             output.addf("VirtualDub.SaveSegmentedAVI(\"%s\", %d, %d);", strCify(szFileOutput), lSpillThreshold, lSpillFrameThreshold);
  1582.         else
  1583.             output.addf("VirtualDub.Save%sAVI(\"%s\");", fCompatibility ? "Compatible" : "", strCify(szFileOutput));
  1584.         output.adds("VirtualDub.Close();");
  1585.  
  1586.         ///////////////////
  1587.  
  1588.         strncpy(vdj->szInputFile, szFileInput, sizeof vdj->szInputFile);
  1589.         strncpy(vdj->szOutputFile, szFileOutput, sizeof vdj->szOutputFile);
  1590.         sprintf(vdj->szName, "Job %d", VDJob::job_number++);
  1591.  
  1592.         vdj->script = output.getscript();
  1593.         vdj->Add();
  1594.     } catch(...) {
  1595.         freemem(vdj);
  1596.         throw;
  1597.     }
  1598. }
  1599.  
  1600. void JobWriteConfiguration(FILE *f, DubOptions *opt) {
  1601.     JobScriptOutput output;
  1602.     char *scr;
  1603.  
  1604.     JobCreateScript(output, opt);
  1605.  
  1606.     scr = output.getscript();
  1607.  
  1608.     if (fputs(scr, f)<0 || fflush(f)) {
  1609.         freemem(scr);
  1610.         throw MyError("Can't write configuration: %s.", strerror(errno));
  1611.     }
  1612.  
  1613.     freemem(scr);
  1614. }
  1615.  
  1616. ///////////////////////////////////////////////////////////////////////////
  1617.  
  1618. bool InitJobSystem() {
  1619.     VDJob::ListLoad();
  1620.  
  1621.     return true;
  1622. }
  1623.  
  1624. void DeinitJobSystem() {
  1625.     VDJob::ListClear(true);
  1626. }
  1627.  
  1628. void OpenJobWindow() {
  1629.     if (g_hwndJobs) return;
  1630.  
  1631.     g_hwndJobs = CreateDialog(g_hInst, MAKEINTRESOURCE(IDD_JOBCONTROL), NULL, JobCtlDlgProc);
  1632. }
  1633.  
  1634. void CloseJobWindow() {
  1635.     if (g_hwndJobs)
  1636.         DestroyWindow(g_hwndJobs);
  1637. }
  1638.  
  1639. void JobLockDubber() {
  1640.     if (g_hwndJobs) {
  1641.         EnableWindow(GetDlgItem(g_hwndJobs, IDC_START), FALSE);
  1642.         EnableWindow(GetDlgItem(g_hwndJobs, IDC_ABORT), FALSE);
  1643.     }
  1644. }
  1645.  
  1646. void JobUnlockDubber() {
  1647.     if (g_hwndJobs) {
  1648.         EnableWindow(GetDlgItem(g_hwndJobs, IDC_START), TRUE);
  1649.         EnableWindow(GetDlgItem(g_hwndJobs, IDC_ABORT), TRUE);
  1650.     }
  1651. }
  1652.  
  1653. void JobPositionCallback(LONG start, LONG cur, LONG end) {
  1654.     char buf[8];
  1655.  
  1656.     if (g_hwndJobs) {
  1657.         SendMessage(GetDlgItem(g_hwndJobs, IDC_PROGRESS), PBM_SETPOS, MulDiv(cur, 16384, end-start), 0);
  1658.         wsprintf(buf, "%d%%", MulDiv(cur, 100, end-start));
  1659.         SetDlgItemText(g_hwndJobs, IDC_PERCENT, buf);
  1660.     }
  1661. }
  1662.  
  1663. void JobClearList() {
  1664.     VDJob::ListClear();
  1665. }
  1666.  
  1667. void JobRunList() {
  1668.     VDJob::RunAll();
  1669. }
  1670.  
  1671. void JobAddBatchDirectory(const char *lpszSrc, const char *lpszDst) {
  1672.     // Scan source directory
  1673.  
  1674.     HANDLE                h;
  1675.     WIN32_FIND_DATA        wfd;
  1676.     char *s, *t;
  1677.     char szSourceDir[MAX_PATH], szDestDir[MAX_PATH];
  1678.  
  1679.     strcpy(szSourceDir, lpszSrc);
  1680.     strcpy(szDestDir, lpszDst);
  1681.  
  1682.     s = szSourceDir;
  1683.     t = szDestDir;
  1684.  
  1685.     if (*s) {
  1686.  
  1687.         // If the path string is just \ or starts with x: or ends in a slash
  1688.         // then don't append a slash
  1689.  
  1690.         while(*s) ++s;
  1691.  
  1692.         if ((s==szSourceDir || s[-1]!='\\') && (!isalpha(szSourceDir[0]) || szSourceDir[1]!=':' || szSourceDir[2]))
  1693.             *s++ = '\\';
  1694.  
  1695.     }
  1696.     
  1697.     if (*t) {
  1698.  
  1699.         // If the path string is just \ or starts with x: or ends in a slash
  1700.         // then don't append a slash
  1701.  
  1702.         while(*t) ++t;
  1703.  
  1704.         if ((t==szDestDir || t[-1]!='\\') && (!isalpha(szDestDir[0]) || szDestDir[1]!=':' || szDestDir[2]))
  1705.             *t++ = '\\';
  1706.  
  1707.     }
  1708.  
  1709.     strcpy(s,"*.*");
  1710.  
  1711.     h = FindFirstFile(szSourceDir,&wfd);
  1712.  
  1713.     if (INVALID_HANDLE_VALUE != h) {
  1714.         do {
  1715.             if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  1716.                 char *t2, *dot = NULL;
  1717.  
  1718.                 strcpy(s, wfd.cFileName);
  1719.                 strcpy(t, wfd.cFileName);
  1720.  
  1721.                 // Replace extension with .avi
  1722.  
  1723.                 t2 = t;
  1724.                 while(*t2) if (*t2++ == '.') dot = t2;
  1725.  
  1726.                 if (dot)
  1727.                     strcpy(dot, "avi");
  1728.                 else
  1729.                     strcpy(t2, ".avi");
  1730.  
  1731.                 // Add job!
  1732.  
  1733.                 JobAddConfiguration(&g_dubOpts, szSourceDir, 0, szDestDir, false, NULL, 0, 0);
  1734.             }
  1735.         } while(FindNextFile(h,&wfd));
  1736.         FindClose(h);
  1737.     }
  1738. }
  1739.  
  1740.